﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ACompany.OrderProcessing.Infrastructure;
using ACompany.OrderProcessing.Model;

namespace ACompany.OrderProcessing.Model
{
    public class Order : BusinessBase<Order>, IAggregateRoot
    {
        protected override void Validate()
        {
            if (_items == null)
            {
                BrokenRules.Add(new BrokenRule("Items", "An order must have at least one order item"));
            }
            else if (_items.Count == 0)
            {
                BrokenRules.Add(new BrokenRule("Items", "An order must have at least one order item"));
            }
            else
            {
                foreach (var item in _items)
                {
                    foreach (var br in item.GetBrokenRules())
                    {
                        this.BrokenRules.Add(br);
                    }
                }
            }

            if (Customer == null)
            {
                BrokenRules.Add(new BrokenRule("Customer", "An order must have a customer"));
            }
            if (DespatchAddress == null)
            {
                BrokenRules.Add(new BrokenRule("DespatchAddress", "An order must have a Despatch Address"));
            }
            if (!Enum.IsDefined(typeof(OrderStatus), this.Status))
            {
                BrokenRules.Add(new BrokenRule("Status", "An order must have a Valid Status"));
            }
        }

        #region Fields

        private Customer _customer;
        public Customer Customer
        {
            get { return _customer; }
            set { _customer = value; }
        }

        private Address _despatchAddress;
        public Address DespatchAddress
        {
            get { return _despatchAddress; }
            set { _despatchAddress = value; }
        }

        private DateTime _orderDate;
        public DateTime OrderDate
        {
            get { return _orderDate; }
            set { _orderDate = value; }
        }

        private OrderStatus _status;
        public OrderStatus Status
        {
            get { return _status; }
            set { _status = value; }
        }

        private readonly IList<Item> _items;

        public ReadOnlyCollection<Item> Items
        {
            get { return new ReadOnlyCollection<Item>(_items); }

        }

        private int _version;

        /// <summary>
        /// for (optimistic) concurrency checking
        /// </summary>
        public int Version
        {
            get { return _version; }
            private set { _version = value; }
        }

        #endregion

        public Order()
        {
            OrderDate = DateTime.Now;
            Status = OrderStatus.Placed;
            _items = new List<Item>();
        }

        #region Methods

        public void Remove(Item item)
        {
            _items.Remove(item);
        }

        public void Add(Product product)
        {
            if (!OrderContains(product))
            {
                Item newItem = new Item();
                newItem.Order = this;
                newItem.Product = product;
                newItem.Qty = 1;
                newItem.Price = product.Price;
                _items.Add(newItem);
            }
            else
            {
                IncreaseQtyOfItemWithMatching(product);
            }

        }

        private void IncreaseQtyOfItemWithMatching(Product product)
        {
            foreach (var item in _items)
            {
                if (item.Product.Equals(product))
                {
                    item.Qty += 1;
                    break;
                }
            }
        }

        private bool OrderContains(Product product)
        {
            foreach (var item in _items)
            {
                if (item.Product.Id == product.Id)
                {
                    return true;
                }
            }
            return false;
        }

        public bool CanCancel()
        {
            return this.Status == OrderStatus.Placed;
        }
        public bool CanProcess()
        {
            if (this.Status == OrderStatus.Placed)
            {
                return HaveEnoughtStockToProcess();
            }
            return false;
        }

        private bool HaveEnoughtStockToProcess()
        {
            foreach (var item in _items)
            {
                if (item.Qty > item.Product.Stock)
                {
                    return false;
                }
            }
            return true;
        }

        public void Cancel()
        {
            if (CanCancel())
            {
                this.Status = OrderStatus.Cancelled;
            }
            else
            {
                throw new InvalidOperationException("this order can not be cancelled");
            }
        }

        public void Process()
        {
            if (CanProcess())
            {
                this.Status = OrderStatus.Shipped;
            }
            else
            {
                throw new InvalidOperationException("this order can not be processed");
            }
        }

        #endregion

        public bool HaveEnoughStockToProcess()
        {

            bool enoughStock = true;

            foreach (Item i in this.Items)
            {
                if (i.Qty > i.Product.Stock)
                {
                    enoughStock = false;
                }
            }

            return enoughStock;

        }

    }
}
